import { useCommand } from "@/utils/useCommand";
import * as _ from "lodash";

import clone from "clone";
import {
  VisualEditorBlockData,
  VisualEditorModleValue
} from "./visualEditor.utils";
import { ElMessageBox } from "element-plus";
import { $$dialog } from "../dialog/dialog-service";

export function useVisualCommand({
  focusData,
  dataModel,
  updataBlocks,
  dragStart,
  dragEnd
}: {
  focusData: {
    value: { focus: VisualEditorBlockData[]; unfocus: VisualEditorBlockData[] };
  };
  dataModel: { value: VisualEditorModleValue | undefined };
  updataBlocks: (blocks: VisualEditorBlockData[]) => void;
  dragStart: { on: (cb: () => void) => void; off: (cb: () => void) => void };
  dragEnd: { on: (cb: () => void) => void; off: (cb: () => void) => void };
}) {
  const commander = useCommand();
  /** 删除 */
  commander.registry({
    name: "delete",
    followQueue: true,
    keyboard: ["ctr+d", "backspace", "delete"],
    execute: () => {
      const { unfocus } = focusData.value;
      const data = {
        before: _.cloneDeep(dataModel.value?.blocks) as VisualEditorBlockData[],
        after: _.cloneDeep(unfocus) as VisualEditorBlockData[]
      };
      return {
        redo: () => {
          //重做
          updataBlocks(_.cloneDeep(data.after));
        },
        undo: () => {
          //撤销
          updataBlocks(_.cloneDeep(data.before));
        }
      };
    }
  });
  /** 撤销 */
  commander.registry({
    name: "undo",
    followQueue: false,
    keyboard: ["ctr+z"],
    execute: () => {
      return {
        redo: () => {
          const current = commander.state.current;
          if (current === -1) return;
          const { undo } = commander.state.queue[current];
          !!undo && undo();
          commander.state.current--;
        }
      };
    }
  });
  /** 反撤销,重做 */
  commander.registry({
    name: "redo",
    followQueue: false,
    keyboard: ["ctr+shift+z", "ctr+y"],
    execute: () => {
      return {
        redo: () => {
          /** 从队列中取出对应的重做函数执行 */
          let current = commander.state.current;
          if (++current === commander.state.queue.length) return;
          const { redo } = commander.state.queue[current];
          !!redo && redo();
          commander.state.current++;
        }
      };
    }
  });
  /** 清空 */
  commander.registry({
    name: "clear",
    followQueue: true,
    keyboard: [],
    execute: () => {
      const data = {
        before: _.cloneDeep(dataModel.value?.blocks) as VisualEditorBlockData[],
        after: _.cloneDeep([]) as VisualEditorBlockData[]
      };
      return {
        redo: () => {
          //重做
          updataBlocks(_.cloneDeep(data.after));
        },
        undo: () => {
          //撤销
          updataBlocks(_.cloneDeep(data.before));
        }
      };
    }
  });
  /** 置顶 */
  commander.registry({
    name: "placedTop",
    followQueue: true,
    keyboard: [],
    execute: () => {
      const data = {
        before: _.cloneDeep(dataModel.value?.blocks) as VisualEditorBlockData[],
        after: (() => {
          /** 获取未选中组件中的最大zIndex，设置选中组件的zIndex比未选中高 */
          const { unfocus, focus } = focusData.value;
          const zIndex = unfocus.reduce((pre: number, next: VisualEditorBlockData) => {
            return Math.max(pre, next.zIndex)
          }, 0) + 1
          focus.forEach(block => block.zIndex = zIndex)
          return _.cloneDeep(dataModel.value?.blocks || [])
        })()
      };
      return {
        redo: () => {
          //重做
          updataBlocks(_.cloneDeep(data.after));
        },
        undo: () => {
          //撤销
          updataBlocks(_.cloneDeep(data.before));
        }
      };
    }
  });
  /** 置底 */
  commander.registry({
    name: "placedBottom",
    followQueue: true,
    keyboard: [],
    execute: () => {
      const data = {
        before: _.cloneDeep(dataModel.value?.blocks) as VisualEditorBlockData[],
        after: (() => {
          const { unfocus, focus } = focusData.value;
          let zIndex = unfocus.reduce((pre: number, next: VisualEditorBlockData) => {
            return Math.min(pre, next.zIndex)
          }, 0) - 1
          if (zIndex < 0) {
            zIndex = Math.abs(zIndex)
            unfocus.forEach(block => block.zIndex = zIndex)
            zIndex = 0
          }
          focus.forEach(block => block.zIndex = zIndex)
          return _.cloneDeep(dataModel.value?.blocks || [])
        })()
      };
      return {
        redo: () => {
          //重做
          updataBlocks(_.cloneDeep(data.after));
        },
        undo: () => {
          //撤销
          updataBlocks(_.cloneDeep(data.before));
        }
      };
    }
  });
  /** 导入 */
  commander.registry({
    name: "import",
    followQueue: true,
    keyboard: [],
    execute: () => {
      const data = {
        before: _.cloneDeep(dataModel.value) as VisualEditorModleValue,
        after: (async () => {
          const text = await $$dialog.textarea('', '请输入倒入的JSON字符串')
          let value = {} as VisualEditorModleValue
          try {
            value = JSON.parse(text || '')
          } catch (e) {
            console.error(e)
            ElMessageBox.alert('解析json字符串出错')
          }
          return _.cloneDeep(value)
        })()
      };
      return {
        redo: async () => {
          //重做
          dataModel.value = await data.after
        },
        undo: () => {
          //撤销
          dataModel.value = data.before
        }
      };
    }
  });
  /**  单个节点导入 */
  commander.registry({
    name: "importBlock",
    followQueue: true,
    keyboard: [],
    execute: () => {
      const data = {
        before: _.cloneDeep(dataModel.value?.blocks || []) as VisualEditorBlockData[],
        after: (async () => {
          const text = await $$dialog.textarea('', '请输入倒入的JSON字符串')
          let value = {} as VisualEditorBlockData
          try {
            value = JSON.parse(text || '')
            const { focus } = focusData.value
            focus.forEach((block, index) => {
                focus[index] = Object.assign(block, value)
            })
          } catch (e) {
            console.error(e)
            ElMessageBox.alert('解析json字符串出错')
          }
          return _.cloneDeep(dataModel.value?.blocks || [])
        })()
      };
      return {
        redo: async () => {
          //重做
          updataBlocks(_.cloneDeep(await data.after));
        },
        undo: () => {
          //撤销
          updataBlocks(_.cloneDeep(data.before));
        }
      };
    }
  });
  /**
   * menu列表拖拽进画布前进行组件数据深克隆，使用撤销时重新赋值
   * menu列表拖拽进画布后松开鼠标进行组件数据深克隆，反撤销时重新赋值
   */
  commander.registry({
    name: "drag",
    followQueue: true,
    keyboard: [],
    init() {
      this.data = {
        before: null as null | VisualEditorBlockData[],
        after: null as null | VisualEditorBlockData[]
      };
      const handler = {
        /** 在menu列表拖拽时把画布中容器数据进行拷贝，当撤销时可以将拷贝的数据赋值进行还原 */
        dragStart: () => this.data!.before = _.cloneDeep(dataModel.value?.blocks || []),
        /** 当menu列表拖拽到画布中并松开鼠标时，触发execute函数，将画布中组件数据拷贝当重做时使用该数据进行还原 */
        dragEnd: () => commander.state.commands.drag()
      };
      /** 订阅，在组件拖拽时发布，调用订阅的函数 */
      dragStart.on(handler.dragStart);
      dragEnd.on(handler.dragEnd);
      return () => {
        dragStart.off(handler.dragStart);
        dragEnd.off(handler.dragEnd);
      };
    },
    execute() {
      /** 拖拽前画布组件数据 */
      const before = _.cloneDeep(this.data!.before || []);
      /** 拖拽结束后画布组件数据 */
      const after = _.cloneDeep(dataModel.value?.blocks || []);
      return {
        redo: () => {
          //重做
          updataBlocks(_.cloneDeep(after));
        },
        undo: () => {
          //撤销
          updataBlocks(_.cloneDeep(before));
        }
      };
    }
  });

  commander.init();
  return {
    delete: commander.state.commands.delete,
    undo: commander.state.commands.undo,
    redo: commander.state.commands.redo,
    clear: commander.state.commands.clear,
    drag: commander.state.commands.drag,
    placedTop: commander.state.commands.placedTop,
    placedBottom: commander.state.commands.placedBottom,
    import: commander.state.commands.import,
    importBlock: commander.state.commands.importBlock,
  };
}
